package org.elj.jeasyexcel.poi;

import java.io.ByteArrayOutputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.logging.Logger;

import org.apache.poi.hssf.usermodel.HSSFCell;
import org.apache.poi.hssf.usermodel.HSSFCellStyle;
import org.apache.poi.hssf.usermodel.HSSFFont;
import org.apache.poi.hssf.usermodel.HSSFRichTextString;
import org.apache.poi.hssf.usermodel.HSSFRow;
import org.apache.poi.hssf.usermodel.HSSFSheet;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.elj.jeasyexcel.generator.ExcelGenerator;
import org.elj.jeasyexcel.instrospection.IntrospectionFacility;
import org.elj.jeasyexcel.structure.Body;
import org.elj.jeasyexcel.structure.Cell;
import org.elj.jeasyexcel.structure.Content;
import org.elj.jeasyexcel.structure.Header;
import org.elj.jeasyexcel.structure.SpreadSheet;
import org.elj.jeasyexcel.structure.Style;

public class PoiExcelGenerator implements ExcelGenerator {

	Logger log = Logger.getLogger(PoiExcelGenerator.class.getName());

	private List<SpreadSheet> sheets = null;

	public PoiExcelGenerator() {
		sheets = new ArrayList<SpreadSheet>(3);
	}

	@Override
	public void addSheet(SpreadSheet newSheet) {
		this.sheets.add(newSheet);
	}

	/**
	 * Generates HSSFWorkbook from the sheets collection
	 * 
	 * @return HSSFWorkbook
	 */
	private HSSFWorkbook generateWorkbook() {
		HSSFWorkbook book = new HSSFWorkbook();
		for (SpreadSheet sheet : sheets) {
			generateSheet(sheet, book);
		}
		return book;
	}

	/**
	 * Fill HSSFSheet from SpreadSheet
	 * 
	 * @param t
	 * @param book
	 */
	private void generateSheet(SpreadSheet sheet, HSSFWorkbook book) {
		HSSFSheet hssfSheet = book.createSheet(sheet.getName());
		// iterate on contents
		List<Content> contents = sheet.getContents();
		int rownum = 0;
		for (Content content : contents) {
			rownum = generateContent(content, hssfSheet, book, rownum);
		}
	}

	/**
	 * Fill sheet with one content
	 * 
	 * @param content
	 * @param hssfSheet
	 * @param currentRownum
	 * @return rownum where generation stopped
	 */
	private int generateContent(Content content, HSSFSheet hssfSheet,
			HSSFWorkbook book, int currentRownum) {

		int currentRow = currentRownum + content.getStartRowNumber();
		Body body = content.getBody();
		currentRow = generateHeader(content, hssfSheet, book, currentRow);
		currentRow = generateBody(body, hssfSheet, currentRow, content
				.getStartColumnNumber());
		setColumnWidth(hssfSheet, content);
		return currentRow;
	}

	private void setColumnWidth(HSSFSheet hssfSheet, Content content) {
		int startColumn = content.getStartColumnNumber();
		int nbColumn = content.getHeader().getHeaders().size();
		int stopColumn = startColumn + nbColumn;
		for (int i = startColumn; i < stopColumn; i++) {
			hssfSheet.autoSizeColumn((short) i);
		}
	}

	private int generateHeader(Content content, HSSFSheet hssfSheet,
			HSSFWorkbook book, int currentRow) {
		Header header = content.getHeader();
		List<? extends Cell> headers = header.getHeaders();
		Style style = header.getHeaderStyle();
		HSSFCellStyle cellStyle = getHssfCellStyle(book, style);
		currentRow = generateRow(hssfSheet, currentRow, content
				.getStartColumnNumber(), cellStyle, headers);
		return currentRow;
	}

	/**
	 * Fill hssfsheet from body's Pojos and attributes names
	 * 
	 * @param body
	 * @param hssfSheet
	 * @param rownum
	 * @param startColumn
	 * @return rownum where generation stopped
	 */
	private int generateBody(Body body, HSSFSheet hssfSheet, int rownum,
			short startColumn) {
		int currentRow = rownum;
		String[] attributes = body.getAttributes();
		if (attributes == null) {
			log.info("no attributes specified");
			return currentRow;
		}
		List<Object> pojos = body.getPojos();
		if (pojos == null) {
			log.fine("no pojos");
			return currentRow;
		}
		String valeur = null;
		short currentColumn = startColumn;
		HSSFRow row = null;
		HSSFCell cell = null;
		HSSFRichTextString cellString = null;
		for (Object pojo : pojos) {
			row = hssfSheet.createRow(currentRow++);
			currentColumn = startColumn;
			for (String attributeName : attributes) {
				cell = row.createCell(currentColumn++);
				valeur = IntrospectionFacility.getMethodReturn(pojo,
						attributeName);
				cellString = new HSSFRichTextString(valeur);
				cell.setCellValue(cellString);
			}
		}
		return currentRow;
	}

	private HSSFCellStyle getHssfCellStyle(HSSFWorkbook book, Style style) {
		HSSFCellStyle result = null;
		if (style == null) {
			return result;
		}
		if (book == null) {
			return result;
		}
		result = book.createCellStyle();
		HSSFFont font = book.createFont();
		if (style.getFontName() != null) {
			font.setFontName(style.getFontName());
		}
		if (style.getFontSize() != null) {
			font.setFontHeightInPoints(style.getFontSize().shortValue());
		}
		if (style.isBold()) {
			font.setBoldweight(HSSFFont.BOLDWEIGHT_BOLD);
		}
		if (style.isItalic()) {
			font.setItalic(style.isItalic());
		}
		if (style.isUnderlined()) {
			font.setUnderline(HSSFFont.U_SINGLE);
		}
		result.setFont(font);
		if (style.isCentered()) {
			result.setAlignment(HSSFCellStyle.ALIGN_CENTER);
		}
		return result;
	}

	/**
	 * @param hssfSheet
	 * @param currentRow
	 * @param startColumn
	 * @param cellStyle
	 * @param cells
	 * @return int last row index written (+1)
	 */
	private int generateRow(HSSFSheet hssfSheet, int currentRow,
			int startColumn, HSSFCellStyle cellStyle, List<? extends Cell> cells) {
		HSSFRow titleRow = hssfSheet.createRow(currentRow++);
		HSSFCell hssfCell = null;
		HSSFRichTextString cellString = null;
		Cell cell = null;
		for (int i = 0; i < cells.size(); i++) {
			cell = cells.get(i);
			hssfCell = titleRow.createCell((short) (i + startColumn));
			cellString = new HSSFRichTextString(cell.getValue());
			hssfCell.setCellValue(cellString);
			if (cellStyle != null) {
				hssfCell.setCellStyle(cellStyle);
			}
		}
		return currentRow;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.elj.jeasyexcel.generator.ExcelGenerator#generateFile(java.lang.String)
	 */
	@Override
	public void generateFile(String filename) {
		HSSFWorkbook generatedWorkbook = generateWorkbook();
		FileOutputStream fos;
		try {
			fos = new FileOutputStream(filename);
			generatedWorkbook.write(fos);
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.elj.jeasyexcel.generator.ExcelGenerator#getFile()
	 */
	@Override
	public byte[] getFile() {
		byte[] result = null;
		HSSFWorkbook generatedWorkbook = generateWorkbook();
		ByteArrayOutputStream baos = null;
		try {
			baos = new ByteArrayOutputStream();
			generatedWorkbook.write(baos);
			result = baos.toByteArray();
		} catch (IOException e) {
			e.printStackTrace();
		}
		return result;
	}

}
